package meminin.id3;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import meminin.model.DataRecord;
import meminin.model.Label;
import meminin.model.MySqlTrainingData;

public class ID3TrainingData extends MySqlTrainingData {

    public void setDataPath(String dataPath) {
        this.dataPath = dataPath;
    }

    public void setLabels(ArrayList<Label> labels) {
        this.labels = labels;
    }

    public String getDataPath() {
        return dataPath;
    }

    public ArrayList<Label> getLabels() {
        return labels;
    }

    protected String dataPath;
    protected ArrayList<Label> labels;
    
    public ID3TrainingData(String filePath, String hostName, String dbName, String tableName, String dbUser, String dbPassword) {
        super(hostName, dbName, tableName, dbUser, dbPassword);
        dataPath = filePath;
        labels = new ArrayList<Label>();
    }

    public double getEnthropy(DataRecord exampleFilter, Label targetLabel) {
        System.out.println("  Get Enthropy (filter : " + exampleFilter + ", target : " + targetLabel.getDesc() + ")");
        double temp = 0;
        ArrayList<String> values = targetLabel.getValues();
        for (String value : values) {
            DataRecord newFilter = exampleFilter.getCopy();
            newFilter.setAttributeValue(targetLabel.getDesc(), value);
            double totalInstance = (getRecordsCount(exampleFilter));
            double subsetInstance = (getRecordsCount(newFilter));
            if (subsetInstance > 0) {
                temp -= ((subsetInstance / totalInstance) * Math.log(subsetInstance / totalInstance) / Math.log((double) 2));
            }
            System.out.println("    Target label value : " + value + ", total instance : " + totalInstance + ", subsetInstance : " + subsetInstance);
        }
        System.out.println("    Enthropy : " + temp);
        return temp;
    }

    public double getInformationGain(DataRecord exampleFilter, Label targetLabel, Label candidateLabel) {
        System.out.println("  Get Information Gain (filter : " + exampleFilter + ", target : " + targetLabel.getDesc() + ", candidate : " + candidateLabel.getDesc() + ")");
        double temp = getEnthropy(exampleFilter, targetLabel);
        // System.out.println("ent awal : "+temp);
        ArrayList<String> candidateValues = candidateLabel.getValues();
        for (String value : candidateValues) {
            DataRecord newFilter = exampleFilter.getCopy();
            newFilter.setAttributeValue(candidateLabel.getDesc(), value);
            double totalInstance = (getRecordsCount(exampleFilter));
            double subsetInstance = (getRecordsCount(newFilter));
            double subsetEnthropy = getEnthropy(newFilter, targetLabel);
            if (subsetInstance > 0) {
                temp -= ((subsetInstance / totalInstance) * subsetEnthropy);
            }
            System.out.println("    Candidate value : " + value + ", total instance : " + totalInstance + ", subset instance " + subsetInstance + " subset enthropy : " + subsetEnthropy);
        }
        System.out.println("    Information Gain : " + temp);
        return temp;
    }

    public Label getMostDistinctiveLabel(DataRecord exampleFilter, ArrayList<Label> labels, Label targetLabel) {
        System.out.println("  Get Most Distinctive Label : ");
        Label tempLabel = null;
        double tempMaxGain = 0;
        for (Label remainingLabel : labels) {
            double tempInformationGain = getInformationGain(exampleFilter, targetLabel, remainingLabel);
            System.out.println("    Candidate : " + remainingLabel.getDesc() + ", with information gain : " + tempInformationGain);
            if (tempInformationGain >= tempMaxGain) {
                tempLabel = remainingLabel;
                tempMaxGain = tempInformationGain;
            }
        }
        return tempLabel;
    }

    private void doRead() {
        try {
            FileInputStream fs = new FileInputStream(dataPath);
            DataInputStream in = new DataInputStream(fs);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String str;
            while ((str = br.readLine()) != null) {
                if (str.length() > 1) {
                    parseData(str);
                }
            }
            in.close();
        } catch (Exception ex) {
            Logger.getLogger(ID3TrainingData.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void parseData(String str) {
        String t = str.toUpperCase();
        if (t.startsWith("%")) {
        } else if (t.startsWith("@RELATION")) {
            String[] split = str.split("\\s+");
            connector.createTable(split[1]);
            tableName = split[1];
        } else if (t.startsWith("@ATTRIBUTE")) {
            String[] split = str.split("\\s+");
            String col = split[split.length - 2];
            String type = split[split.length - 1];
            ID3Label tempLabel = new ID3Label(col);
            ArrayList<String> labelValues = new ArrayList<String>();
            if (type.startsWith("{") && type.endsWith("}")) { // check if type = enum
                String tmp = type.substring(1, type.length() - 1);
                String[] enumString = tmp.split(",");
                type = "('";
                for (int i = 0; i < enumString.length; i++) {
                    type = type.concat(enumString[i]).concat("','");
                    labelValues.add(enumString[i]);
                }
                type = type.substring(0, type.length() - 2).concat(")");
                connector.addColumn(col, "ENUM", type);
            } else if (type.compareToIgnoreCase("numeric") == 0) {
                // numeric attribute : float
                connector.addColumn(col, "FLOAT", "(20)");
            } else if (type.compareToIgnoreCase("string") == 0) {
                // string attribute  
                connector.addColumn(col, "VARCHAR", "(20)");
            } else if (type.compareToIgnoreCase("date") == 0) {
                // date attribute  
                // not implemented yet
            }
            tempLabel.setValues(labelValues);
            labels.add(tempLabel);
        } else if (t.startsWith("@DATA")) {
            connector.prepareInsert();
        } else {
            String[] split = str.split(",");
            connector.insertRow(split);
        }
    }

    private void getData() {
        try {
            ResultSet rs = connector.getTableData();
            while (rs.next()) {
                int colType = rs.getType();
                System.out.println("Column 1 is type " + colType);
            }
        } catch (SQLException ex) {
            Logger.getLogger(ID3TrainingData.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void readTrainingData() {
        doRead();
        getData();
    }
}
